#!/usr/bin/python
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""A simple web server for cell modem tests

Used for:
  Connectivity tests
  Bandwidth tests
"""

import BaseHTTPServer
import socket
import struct
import urlparse

import numpy.random


_MAX_SINGLE_TRANSFER = 1 << 20


def WriteBytes(out, n):
  """Write n random bytes to out."""
  remaining = n

  while remaining:
    if remaining > _MAX_SINGLE_TRANSFER:
      # We use random bytes to defeat any compression that may be
      # present.  This code using the random module was on the edge of
      # too slow:
      #     struct.pack('Q' * (n/8),
      #                 *[random.getrandbits(64) for x in xrange(n/8)])
      # so we use numpy.random.bytes instead.
      out.write(numpy.random.bytes(_MAX_SINGLE_TRANSFER))
      remaining -= _MAX_SINGLE_TRANSFER
    else:
      out.write(numpy.random.bytes(remaining))
      remaining = 0


class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):
  def address_string(self):
    """Build address string without attempting a reverse DNS lookup."""
    return str(self.client_address[0])

  def _Dispatch(self, command):
    """Parses query and dispatches a request to the relevant method."""
    parsed = urlparse.urlparse(self.path)

    # the last value specified for each key
    self.query = dict(
        [(k, v[-1]) for k,v in urlparse.parse_qs(parsed.query).items()])

    dispatch_table = {
        'GET': {
            '/connectivity/index.html': self.Connectivity,
            '/download': self.GetBytes,
            },
        'POST': {
            '/upload': self.SinkBytes,
            },
        }
    if parsed.path not in dispatch_table[command]:
      self.send_error(404)
      return
    dispatch_table[command][parsed.path]()

  def do_GET(self):
    return self._Dispatch('GET')

  def do_POST(self):
    return self._Dispatch('POST')

  def GetBytes(self):
    try:
      n = int(self.query['size'])
    except KeyError:
      self.send_error(400)
      return
    self.send_response(200)
    self.send_header('Content-Length', n)
    self.end_headers()
    WriteBytes(self.wfile, n)

  def SinkBytes(self):
    remaining = int(self.headers['Content-Length'])

    while remaining:
      this_read = min(remaining, _MAX_SINGLE_TRANSFER)
      got = self.rfile.read(this_read)
      if not got:
        break
      remaining -= len(got)

    self.send_response(200)
    self.end_headers()

  def Connectivity(self):
    self.send_response(200)
    self.send_header('Content-Type', 'text/plain')
    self.end_headers()
    self.wfile.write('Chromium')


class BigBufferHTTPServer(BaseHTTPServer.HTTPServer):
  """HTTP server with large buffer sizes.

  Empirically, we see poor performance with cell emulators and default
  buffer sizes.
  """

  def get_request(self):
    """Overrides get_request to set buffer sizes.
    Returns:
        (socket, address), where socket has been modified for large buffers
    """
    # We can't use super() here because BaseHTTPServer.HTTPServer is an
    # old-style class.
    sock, addr = BaseHTTPServer.HTTPServer.get_request(self)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1 << 24)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1 << 24)
    return sock, addr


BigBufferHTTPServer(('', 80), HttpHandler).serve_forever()
